package clipboard

import (
	"fmt"
	"gmap-backend/internal/config"
	"gmap-backend/internal/server/manager"
	"gmap-backend/internal/server/vo"
	"gmap-backend/internal/util/commonutil"
	"gmap-backend/internal/util/fileutil"
	"gmap-backend/internal/util/htmlutil"
	"gmap-backend/internal/util/httputil"
	"gmap-backend/internal/util/imgutil"
	"gmap-backend/internal/util/pathutil"
	"os"
	"path/filepath"
	"regexp"
	"strings"
	"time"
)

var urlfmt = regexp.MustCompile("^[a-zA-Z0-9_-]+?[:]//.+$")

// GetClipPossibleOp 判断剪切板可能进行的操作，不进行具体的判断，只判断剪切板有对应类型的内容即认为可能操作
func GetClipPossibleOp(appConfig *config.AppConfig, req *vo.BaseActionReq, bins [][]byte) *vo.ClipPossibleOpResp {
	hasDropFiles := len(manager.GetClipFiles(appConfig)) > 0
	hasTxt := "" != strings.TrimSpace(manager.GetClipTxt(appConfig))
	hasImgObj := manager.GetClipHasImgObj(appConfig)

	ret := &vo.ClipPossibleOpResp{}
	if hasDropFiles || hasTxt {
		ret.GetUrl = true
		ret.GetImgUrl = true
	}
	if hasDropFiles || hasTxt || hasImgObj {
		ret.ImgToLocal = true
		ret.ImgToRemote = true
		ret.FileToLocal = true
		ret.FileToRemote = true
	}
	return ret
}

func GetUrlFromClipboard(appConfig *config.AppConfig, req *vo.BaseActionReq, bins [][]byte) *vo.TitleUrlResp {
	// 剪切板有文件列表，取第一个生成url
	files := manager.GetClipFiles(appConfig)
	if len(files) > 0 {
		parseResult := pathutil.ParseValidPath(files[0], "")
		if parseResult.Succ {
			url := "file:///" + strings.ReplaceAll(parseResult.ValidPath, "\\", "/")
			name := filepath.Base(parseResult.ValidPath)
			return &vo.TitleUrlResp{
				Title: name,
				Url:   url,
			}
		}
	}

	// 剪切板有文本，如果是网址，则处理一下格式后返回，如果是其它，按路径解析后生成url
	txt := strings.TrimSpace(manager.GetClipTxt(appConfig))
	if "" != txt {
		// 网址
		isWebsite := false
		webUrl := ""
		if strings.HasPrefix(txt, "http://") || strings.HasPrefix(txt, "https://") {
			isWebsite = true
			webUrl = txt
		} else if strings.HasPrefix(txt, "//") && len([]rune(txt)) > 2 {
			isWebsite = true
			webUrl = "https:" + txt
		} else if strings.HasPrefix(txt, "www.") && len([]rune(txt)) > 4 {
			isWebsite = true
			webUrl = "https://" + txt
		}
		if isWebsite {
			// 请求指定的网址并获取标题
			html := httputil.GetHtmlContentWithRedirect(webUrl, config.DEFAULT_REQ_TIMEOUT, config.DEFAULT_UA)
			if !html.Succ {
				panic("get html err")
			}
			title := htmlutil.GetTitle(html.Content)
			if "" == title {
				title = "网址"
			}
			return &vo.TitleUrlResp{
				Title: title,
				Url:   webUrl,
			}
		}

		// 是其它协议
		if urlfmt.MatchString(txt) {
			return &vo.TitleUrlResp{
				Title: "地址",
				Url:   txt,
			}
		}

		// 解析路径并返回
		parseResult := pathutil.ParseValidPath(txt, "")
		if parseResult.Succ {
			url := "file:///" + strings.ReplaceAll(parseResult.ValidPath, "\\", "/")
			return &vo.TitleUrlResp{
				Title: filepath.Base(parseResult.ValidPath),
				Url:   url,
			}
		}
	}
	panic("未能从剪切板找到url地址")
}

// GetImgUrlFromClipboard 从剪切板获得图片url
func GetImgUrlFromClipboard(appConfig *config.AppConfig, req *vo.BaseActionReq, bins [][]byte) *vo.TitleUrlResp {
	// 剪切板有文件，取第一个生成测试是否为图片，如果是则返回结果
	files := manager.GetClipFiles(appConfig)
	if len(files) > 0 {
		parseResult := pathutil.ParseValidPath(files[0], "")
		if parseResult.Succ && parseResult.File {
			imgValid, _ := imgutil.IsValidImgFile(parseResult.ValidPath)
			if imgValid {
				url := "file:///" + strings.ReplaceAll(parseResult.ValidPath, "\\", "/")
				name := filepath.Base(parseResult.ValidPath)
				return &vo.TitleUrlResp{
					Title: name,
					Url:   url,
				}
			}
		}
	}

	// 剪切板有文本，如果是网址，则处理一下格式后返回，如果是其它，按路径解析后生成url
	txt := strings.TrimSpace(manager.GetClipTxt(appConfig))
	if "" != txt {
		// 网址
		isWebsite := false
		webUrl := ""
		if strings.HasPrefix(txt, "http://") || strings.HasPrefix(txt, "https://") {
			isWebsite = true
			webUrl = txt
		} else if strings.HasPrefix(txt, "//") && len([]rune(txt)) > 2 {
			isWebsite = true
			webUrl = "https:" + txt
		} else if strings.HasPrefix(txt, "www.") && len([]rune(txt)) > 4 {
			isWebsite = true
			webUrl = "https://" + txt
		}
		if isWebsite {
			return &vo.TitleUrlResp{
				Title: "图片",
				Url:   webUrl,
			}
		}

		// 解析路径并返回
		parseResult := pathutil.ParseValidPath(txt, "")
		if parseResult.Succ && parseResult.File {
			imgValid, _ := imgutil.IsValidImgFile(parseResult.ValidPath)
			if imgValid {
				url := "file:///" + strings.ReplaceAll(parseResult.ValidPath, "\\", "/")
				return &vo.TitleUrlResp{
					Title: filepath.Base(parseResult.ValidPath),
					Url:   url,
				}
			}
		}
	}
	panic("未能从剪切板找到图片url地址")
}

func SaveClipImgToLocal(appConfig *config.AppConfig, req *vo.PathReq, bins [][]byte) *vo.TitleUrlResp {
	return saveClipFileTo(appConfig, req, false, true)
}

func SaveClipFileToLocal(appConfig *config.AppConfig, req *vo.PathReq, bins [][]byte) *vo.TitleUrlResp {
	return saveClipFileTo(appConfig, req, false, false)
}

func SaveClipImgToRemote(appConfig *config.AppConfig, req *vo.PathReq, bins [][]byte) *vo.TitleUrlResp {
	return saveClipFileTo(appConfig, req, true, true)
}

func SaveClipFileToRemote(appConfig *config.AppConfig, req *vo.PathReq, bins [][]byte) *vo.TitleUrlResp {
	return saveClipFileTo(appConfig, req, true, false)
}

func saveClipFileTo(appConfig *config.AppConfig, req *vo.PathReq, saveRemote bool, shouldImg bool) *vo.TitleUrlResp {
	// 剪切板有文件列表，取第一个生成url
	if succ, originFn, newFn := saveClipHdrop(appConfig, req.Path, saveRemote, shouldImg); succ {
		return &vo.TitleUrlResp{
			Title: originFn,
			Url:   newFn,
		}
	}

	// 剪切板中的图片对象保存为文件（目前只支持保存为bmp位图），由于图片对象没有文件名，所以只能随机命名
	// 如果本地保存，则直接保存到指定目录
	// 如果要保存到图床，则先保存到临时目录，再上传到图床，由于固定是图片，不会区分上传位置是图片还是附件（一定是图片）
	{
		filename := commonutil.GetUuid() + ".bmp"
		savePath := ""
		if !saveRemote {
			savePath = filepath.Join(req.Path, filename)
		} else {
			savePath = filepath.Join(appConfig.PathConfig.WorkDir, filename)
		}
		if manager.SaveClipImgToFile(appConfig, savePath) {
			if !saveRemote {
				return &vo.TitleUrlResp{
					Title: filename,
					Url:   "assets/" + filename,
				}
			}
			if succ, refUrl := manager.UploadImg(appConfig, savePath); succ {
				return &vo.TitleUrlResp{
					Title: filename,
					Url:   refUrl,
				}
			}
		}
	}

	// 剪切板的文本按文件名、网址、图片base64识别
	if txt := manager.GetClipTxt(appConfig); "" != txt {
		if succ, originFn, newFn := saveByFilePath(appConfig, txt, req.Path, saveRemote, shouldImg); succ {
			return &vo.TitleUrlResp{
				Title: originFn,
				Url:   newFn,
			}
		}

		// 如果要保存图片，还要按url判断，如果是网址，并且下载后是有效图片，则保存
		if shouldImg && (strings.HasPrefix(txt, "http://") || strings.HasPrefix(txt, "https://")) {
			tmpSavePath := filepath.Join(appConfig.PathConfig.WorkDir, commonutil.GetUuid())
			if downRes := httputil.Download(txt, tmpSavePath, config.DEFAULT_REQ_TIMEOUT, config.DEFAULT_UA); downRes.Succ {
				if succ, originFn, newFn := saveByFilePath(appConfig, tmpSavePath, req.Path, saveRemote, true); succ {
					return &vo.TitleUrlResp{
						Title: originFn,
						Url:   newFn,
					}
				}
			}
		}

		if succ, imgType, bs := imgutil.GetBase64ImgInfo(txt, shouldImg); succ {
			filename := commonutil.GetUuid() + "." + imgType
			saveFullPath := filepath.Join(req.Path, filename)
			if saveRemote {
				saveFullPath = filepath.Join(appConfig.PathConfig.WorkDir, filename)
			}
			os.WriteFile(saveFullPath, bs, 0666)
			if !saveRemote {
				return &vo.TitleUrlResp{
					Title: filename,
					Url:   "assets/" + filename,
				}
			}
			if upSucc, refUrl := manager.UploadImg(appConfig, saveFullPath); upSucc {
				return &vo.TitleUrlResp{
					Title: filename,
					Url:   refUrl,
				}
			}
		}
	}
	fileType := "文件"
	if shouldImg {
		fileType = "图片"
	}
	panic("未能从剪切板获取" + fileType)
}

func saveClipHdrop(appConfig *config.AppConfig, saveDir string, saveRemote bool, shouldImg bool) (bool, string, string) {
	files := manager.GetClipFiles(appConfig)
	if 0 == len(files) {
		return false, "", ""
	}
	for _, file := range files {
		if succ, originFn, newFn := saveByFilePath(appConfig, file, saveDir, saveRemote, shouldImg); succ {
			return true, originFn, newFn
		}
	}
	return false, "", ""
}

// bool 成功失败标识
// string 用于显示的名称
// string url引用
func saveByFilePath(appConfig *config.AppConfig, file string, saveDir string, saveRemote bool, shouldImg bool) (bool, string, string) {
	parseResult := pathutil.ParseValidPath(file, "")
	if !parseResult.Succ || parseResult.Dir {
		return false, "", ""
	}
	fun := func(newFileExt string) (bool, string, string) {
		// 保存到图床：图片或附件
		if saveRemote {
			if shouldImg {
				if succ, refUrl := manager.UploadImg(appConfig, parseResult.ValidPath); succ {
					return true, filepath.Base(parseResult.ValidPath), refUrl
				}
				return false, "", ""
			}
			if succ, refUrl := manager.UploadAtt(appConfig, parseResult.ValidPath); succ {
				return true, filepath.Base(parseResult.ValidPath), refUrl
			}
			return false, "", ""
		}

		// 保存到本地
		fn := filepath.Base(parseResult.ValidPath)
		fn = strings.TrimSuffix(fn, filepath.Ext(parseResult.ValidPath))
		localSavePath := filepath.Join(saveDir, fn+newFileExt)
		if fileutil.Exists(localSavePath) {
			fn = fmt.Sprintf("%s_%v", fn, time.Now().UnixNano())
			localSavePath = filepath.Join(saveDir, fn+newFileExt)
		}
		fileutil.CopyFile(parseResult.ValidPath, localSavePath)
		return true, fn + newFileExt, "assets/" + fn + newFileExt
	}
	if !shouldImg {
		return fun(filepath.Ext(parseResult.ValidPath))
	}
	if validImg, imgType := imgutil.IsValidImgFile(parseResult.ValidPath); validImg {
		return fun("." + imgType)
	}
	return false, "", ""
}
